import requests
import json
import time
import threading
from datetime import datetime, timedelta
from plyer import notification
import os
import logging
from queue import Queue
from collections import defaultdict
import sys
import platform
import random
from typing import Dict, Set, Any, Optional, Tuple
import configparser


class ConfigManager:
    """配置管理器"""

    def __init__(self, config_file="config.ini"):
        self.config_file = config_file
        self.config = configparser.ConfigParser()
        self.load_config()

    def load_config(self):
        """加载配置文件"""
        if not os.path.exists(self.config_file):
            self.create_default_config()

        self.config.read(self.config_file, encoding='utf-8')

    def create_default_config(self):
        """创建默认配置文件"""
        config = configparser.ConfigParser()

        # 基础设置
        config.add_section('基础设置')
        config.set('基础设置', '监控间隔秒数', '10')
        config.set('基础设置', '通知间隔秒数', '15')
        config.set('基础设置', '通知显示时间秒数', '180')
        config.set('基础设置', '过滤ST股票', 'true')
        config.set('基础设置', '最小板块股票数', '2')

        # 网络设置
        config.add_section('网络设置')
        config.set('网络设置', '请求超时秒数', '15')
        config.set('网络设置', '最大重试次数', '100')
        config.set('网络设置', '基础重试延迟秒数', '2')

        # 代理设置
        config.add_section('代理设置')
        config.set('代理设置', '启用代理', 'true')
        config.set('代理设置', '代理类型', 'socks5')
        config.set('代理设置', '代理地址', '127.0.0.1')
        config.set('代理设置', '代理端口', '8442')

        # 日志设置
        config.add_section('日志设置')
        config.set('日志设置', '日志级别', 'INFO')
        config.set('日志设置', '日志文件名', 'stock_monitor.log')
        config.set('日志设置', '控制台输出', 'true')

        # 通知设置
        config.add_section('通知设置')
        config.set('通知设置', '启用桌面通知', 'true')
        config.set('通知设置', '启用声音提醒', 'true')
        config.set('通知设置', '启用控制台输出', 'true')

        with open(self.config_file, 'w', encoding='utf-8') as f:
            config.write(f)

        print(f"✅ 已创建默认配置文件: {self.config_file}")
        print("💡 您可以编辑配置文件来自定义设置")

    def get_bool(self, section: str, key: str, default: bool = False) -> bool:
        """获取布尔值配置"""
        try:
            return self.config.getboolean(section, key)
        except:
            return default

    def get_int(self, section: str, key: str, default: int = 0) -> int:
        """获取整数配置"""
        try:
            return self.config.getint(section, key)
        except:
            return default

    def get_str(self, section: str, key: str, default: str = '') -> str:
        """获取字符串配置"""
        try:
            return self.config.get(section, key)
        except:
            return default


class StockMonitor:
    def __init__(self, config_file="config.ini"):
        # 加载配置
        self.config_manager = ConfigManager(config_file)
        self.load_settings()

        # HTTP请求头
        self.headers = {
            "accept": "application/json, text/plain, */*",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,ar;q=0.7",
            "cache-control": "no-cache",
            "origin": "https://xuangutong.com.cn",
            "pragma": "no-cache",
            "priority": "u=1, i",
            "referer": "https://xuangutong.com.cn/dingpan",
            "sec-ch-ua": "\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Microsoft Edge\";v=\"138\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "cross-site",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0"
        }

        self.url = "https://flash-api.xuangubao.com.cn/api/pool/detail"
        self.params = {"pool_name": "limit_up"}

        # 网络状态统计
        self.network_stats = {
            'total_requests': 0,
            'successful_requests': 0,
            'failed_requests': 0,
            'last_success_time': None,
            'consecutive_failures': 0
        }

        # 日期管理
        self.current_date = datetime.now().date()

        # 基准数据（按板块存储）{plate_id: {stock_symbols}}
        self.baseline_plates = defaultdict(set)
        self.baseline_set = False
        self.plate_names = {}  # {plate_id: plate_name}
        self.alerted_plates = set()  # 记录已经预警过的板块ID

        # 通知队列和管理
        self.notification_queue = Queue()
        self.last_notification_time = datetime.min

        # 设置日志
        self._setup_logging()

        # 检查通知系统
        if self.enable_desktop_notification:
            self._check_notification_system()

        # 启动通知处理线程
        self.notification_thread = threading.Thread(target=self._process_notifications, daemon=True)
        self.notification_thread.start()

        # 加载基准数据
        self.load_baseline_data()

    def load_settings(self):
        """从配置文件加载设置"""
        # 基础设置
        self.monitor_interval = self.config_manager.get_int('基础设置', '监控间隔秒数', 10)
        self.notification_interval = self.config_manager.get_int('基础设置', '通知间隔秒数', 15)
        self.notification_timeout = self.config_manager.get_int('基础设置', '通知显示时间秒数', 180)
        self.filter_st_stocks = self.config_manager.get_bool('基础设置', '过滤ST股票', True)
        self.min_plate_stocks = self.config_manager.get_int('基础设置', '最小板块股票数', 2)

        # 网络设置
        self.request_timeout = self.config_manager.get_int('网络设置', '请求超时秒数', 15)
        self.max_retries = self.config_manager.get_int('网络设置', '最大重试次数', 100)
        self.retry_delay = self.config_manager.get_int('网络设置', '基础重试延迟秒数', 2)

        # 代理设置
        self.enable_proxy = self.config_manager.get_bool('代理设置', '启用代理', True)
        if self.enable_proxy:
            proxy_type = self.config_manager.get_str('代理设置', '代理类型', 'socks5')
            proxy_host = self.config_manager.get_str('代理设置', '代理地址', '127.0.0.1')
            proxy_port = self.config_manager.get_int('代理设置', '代理端口', 8442)
            proxy_url = f"{proxy_type}://{proxy_host}:{proxy_port}"
            self.proxies = {
                'http': proxy_url,
                'https': proxy_url
            }
        else:
            self.proxies = None

        # 日志设置
        self.log_level = self.config_manager.get_str('日志设置', '日志级别', 'INFO')
        self.log_file = self.config_manager.get_str('日志设置', '日志文件名', 'stock_monitor.log')
        self.console_output = self.config_manager.get_bool('日志设置', '控制台输出', True)

        # 通知设置
        self.enable_desktop_notification = self.config_manager.get_bool('通知设置', '启用桌面通知', True)
        self.enable_sound_alert = self.config_manager.get_bool('通知设置', '启用声音提醒', True)
        self.enable_console_output = self.config_manager.get_bool('通知设置', '启用控制台输出', True)

    def get_today_data_file(self):
        """获取今日数据文件名"""
        today_str = datetime.now().strftime("%Y%m%d")
        return f"stock_monitor_baseline_{today_str}.json"

    def _setup_logging(self):
        """设置日志"""
        log_level = getattr(logging, self.log_level.upper(), logging.INFO)
        handlers = [logging.FileHandler(self.log_file, encoding='utf-8')]

        if self.console_output:
            handlers.append(logging.StreamHandler())

        logging.basicConfig(
            level=log_level,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=handlers
        )
        self.logger = logging.getLogger(__name__)

    def _check_notification_system(self):
        """检查桌面通知系统"""
        try:
            self.logger.info("正在测试桌面通知功能...")
            notification.notify(
                title="📊 股票监控系统",
                message="系统启动成功！桌面通知功能正常",
                timeout=5,
                app_name="股票监控系统"
            )
            self.logger.info("桌面通知功能测试完成")
            if self.enable_console_output:
                print("🔔 桌面通知功能已启用")
        except Exception as e:
            self.logger.warning(f"桌面通知测试失败: {e}")
            if self.enable_console_output:
                print(f"⚠️ 桌面通知可能无法正常工作: {e}")
                self._print_notification_tips()

    def _print_notification_tips(self):
        """打印通知设置提示"""
        print("💡 请确保已安装 plyer 库: pip install plyer")
        if platform.system() == "Windows":
            print("💡 Windows用户：请检查通知和操作设置 > 通知")
        elif platform.system() == "Darwin":
            print("💡 macOS用户：请检查系统偏好设置 > 通知")
        elif platform.system() == "Linux":
            print("💡 Linux用户：请确保安装了通知服务 (libnotify)")

    def load_baseline_data(self):
        """加载基准数据"""
        try:
            data_file = self.get_today_data_file()
            if os.path.exists(data_file):
                with open(data_file, "r", encoding="utf-8") as f:
                    data = json.load(f)

                # 加载基准板块数据
                baseline_data = data.get('baseline_plates', {})
                for plate_id, stocks in baseline_data.items():
                    self.baseline_plates[int(plate_id)] = set(stocks)

                # 加载板块名称
                self.plate_names = {int(k): v for k, v in data.get('plate_names', {}).items()}
                self.baseline_set = data.get('baseline_set', False)

                # 加载已预警板块记录
                alerted_data = data.get('alerted_plates', [])
                self.alerted_plates = set(int(plate_id) for plate_id in alerted_data)
                total_stocks = sum(len(stocks) for stocks in self.baseline_plates.values())
                self.logger.info(f"加载今日基准数据成功，共{len(self.baseline_plates)}个板块，{total_stocks}只股票")
            else:
                self.logger.info("今日首次运行，需要设置基准数据")
        except Exception as e:
            self.logger.error(f"加载基准数据失败: {e}")

    def save_baseline_data(self):
        """保存基准数据"""
        try:
            data_file = self.get_today_data_file()

            # 转换数据格式用于JSON序列化
            baseline_data = {str(plate_id): list(stocks) for plate_id, stocks in self.baseline_plates.items()}
            plate_names_data = {str(k): v for k, v in self.plate_names.items()}

            data = {
                "baseline_plates": baseline_data,
                "plate_names": plate_names_data,
                "baseline_set": self.baseline_set,
                "alerted_plates": list(self.alerted_plates),
                "baseline_time": datetime.now().isoformat(),
                "date": self.current_date.isoformat(),
            }

            with open(data_file, "w", encoding="utf-8") as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            self.logger.error(f"保存基准数据失败: {e}")

    def fetch_stock_data(self) -> Optional[Dict[str, Any]]:
        """获取股票数据（带重试机制）"""
        self.network_stats['total_requests'] += 1

        for attempt in range(self.max_retries):
            try:
                # 计算重试延迟（指数退避 + 随机抖动）
                if attempt > 0:
                    delay = self.retry_delay * (2 ** (attempt - 1)) + random.uniform(0, 1)
                    self.logger.info(f"第{attempt + 1}次尝试，延迟{delay:.1f}秒...")
                    time.sleep(delay)

                self.logger.info(f"正在获取数据... (尝试 {attempt + 1}/{self.max_retries})")

                response = requests.get(
                    self.url,
                    headers=self.headers,
                    params=self.params,
                    proxies=self.proxies,
                    timeout=self.request_timeout
                )

                if response.status_code == 200:
                    data = response.json()
                    if self._validate_response_data(data):
                        self.network_stats['successful_requests'] += 1
                        self.network_stats['last_success_time'] = datetime.now()
                        self.network_stats['consecutive_failures'] = 0
                        self.logger.info(f"数据获取成功 (尝试 {attempt + 1}/{self.max_retries})")
                        return data
                    else:
                        self.logger.warning(f"响应数据格式无效 (尝试 {attempt + 1}/{self.max_retries})")
                        continue
                else:
                    self.logger.warning(
                        f"API请求失败，状态码: {response.status_code} (尝试 {attempt + 1}/{self.max_retries})")
                    continue

            except requests.exceptions.Timeout as e:
                self.logger.warning(f"请求超时 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                continue
            except requests.exceptions.ProxyError as e:
                self.logger.warning(f"代理连接错误 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                continue
            except requests.exceptions.ConnectionError as e:
                self.logger.warning(f"网络连接错误 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                continue
            except requests.exceptions.RequestException as e:
                self.logger.warning(f"网络请求异常 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                continue
            except json.JSONDecodeError as e:
                self.logger.warning(f"JSON解析错误 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                continue
            except Exception as e:
                self.logger.error(f"未知错误 (尝试 {attempt + 1}/{self.max_retries}): {e}")
                continue

        # 所有重试都失败了
        self.network_stats['failed_requests'] += 1
        self.network_stats['consecutive_failures'] += 1
        self.logger.error(f"所有重试都失败了 ({self.max_retries}次尝试)")

        # 如果连续失败次数过多，给出建议
        if self.network_stats['consecutive_failures'] >= 5:
            self.logger.warning("连续失败次数过多，建议检查网络连接或代理设置")
            if self.enable_console_output:
                print("⚠️ 连续网络失败，建议检查：")
                print("   1. 网络连接是否正常")
                print("   2. 代理设置是否正确")
                print("   3. 防火墙或安全软件是否阻止连接")

        return None

    def _validate_response_data(self, data: Any) -> bool:
        """验证响应数据格式"""
        if not isinstance(data, dict):
            return False

        if 'data' not in data:
            return False

        if not isinstance(data['data'], list):
            return False

        return True

    def is_st_stock(self, stock_name: str) -> bool:
        """判断是否为ST股票"""
        if not self.filter_st_stocks:
            return False

        if not stock_name or not isinstance(stock_name, str):
            return False
        st_keywords = ['ST', '*ST', 'PT']
        return any(keyword in stock_name.upper() for keyword in st_keywords)

    def parse_stock_data(self, raw_data: Dict[str, Any]) -> Tuple[Dict[int, Set[str]], Dict[str, Dict[str, Any]]]:
        """解析股票数据，按板块分组"""
        current_plates = defaultdict(set)
        stock_details = {}

        if not raw_data or 'data' not in raw_data:
            self.logger.warning("响应数据为空或格式不正确")
            return current_plates, stock_details

        try:
            data = raw_data['data']
            if not isinstance(data, list):
                self.logger.warning("数据格式不正确：data字段不是列表")
                return current_plates, stock_details

            for stock_info in data:
                if not isinstance(stock_info, dict):
                    continue

                # 检查必要字段
                if 'surge_reason' not in stock_info:
                    continue

                surge_reason = stock_info['surge_reason']
                if not isinstance(surge_reason, dict) or 'symbol' not in surge_reason:
                    continue

                symbol = surge_reason['symbol']
                if not symbol:
                    continue

                stock_name = stock_info.get('stock_chi_name', '')

                # 过滤ST股
                if self.is_st_stock(stock_name):
                    continue

                # 获取板块信息
                related_plates = surge_reason.get('related_plates', [])
                if not isinstance(related_plates, list):
                    continue

                # 保存股票详细信息
                stock_details[symbol] = {
                    'name': stock_name,
                    'symbol': symbol,
                    'reason': surge_reason.get('stock_reason', ''),
                    'related_plates': related_plates
                }

                # 按板块分组
                for plate in related_plates:
                    if not isinstance(plate, dict):
                        continue

                    plate_id = plate.get('plate_id')
                    plate_name = plate.get('plate_name', '')

                    if plate_id is not None and isinstance(plate_id, (int, str)):
                        try:
                            plate_id = int(plate_id)
                            current_plates[plate_id].add(symbol)
                            # 更新板块名称映射
                            if plate_id not in self.plate_names:
                                self.plate_names[plate_id] = plate_name
                        except (ValueError, TypeError):
                            continue

        except Exception as e:
            self.logger.error(f"解析数据时发生错误: {e}")

        return current_plates, stock_details

    def _process_notifications(self):
        """通知处理线程"""
        while True:
            try:
                notification_data = self.notification_queue.get(timeout=1)

                # 检查与上次通知的时间间隔
                now = datetime.now()
                time_since_last = (now - self.last_notification_time).total_seconds()

                if time_since_last < self.notification_interval:
                    sleep_time = self.notification_interval - time_since_last
                    time.sleep(sleep_time)

                # 发送通知
                self._send_notification(notification_data)
                self.last_notification_time = datetime.now()
                self.notification_queue.task_done()

            except:
                continue

    def _send_notification(self, notification_data: Dict[str, Any]):
        """发送桌面通知"""
        try:
            current_time = datetime.now().strftime('%H:%M:%S')

            # 构建通知内容
            if notification_data['type'] == 'new_stocks':
                plate_name = notification_data['plate_name']
                count = notification_data['count']
                stock_details = notification_data.get('stock_details', [])

                if count >= 3:
                    title = f"🚨 板块爆发 - {current_time}"
                    message = f"{plate_name} 板块新增 {count} 只涨停股票！"
                else:
                    title = f"🔥 板块涨停 - {current_time}"
                    message = f"{plate_name} 板块新增 {count} 只涨停股票"

                # 添加股票详情
                if stock_details:
                    message += f"\n股票: {', '.join(stock_details[:3])}"
                    if len(stock_details) > 3:
                        message += f" 等{len(stock_details)}只"

            elif notification_data['type'] == 'multi_plate':
                plate_count = notification_data['plate_count']
                total_count = notification_data['total_count']
                plate_details = notification_data.get('plate_details', [])

                title = f"🚨 多板块爆发 - {current_time}"
                message = f"发现 {plate_count} 个板块共 {total_count} 只新涨停股票！"

                # 添加板块详情
                if plate_details:
                    message += f"\n板块: {', '.join(plate_details[:3])}"
                    if len(plate_details) > 3:
                        message += f" 等"

            elif notification_data['type'] == 'network_error':
                title = f"⚠️ 网络异常 - {current_time}"
                message = notification_data.get('message', '网络连接异常')

            # 发送桌面通知
            if self.enable_desktop_notification:
                try:
                    notification.notify(
                        title=title,
                        message=message,
                        timeout=self.notification_timeout,  # 使用配置的超时时间（3分钟）
                        app_name="股票监控系统"
                    )
                    self.logger.info(f"桌面通知已发送: {title}")
                except Exception as notify_error:
                    self.logger.error(f"发送桌面通知失败: {notify_error}")
                    if self.enable_sound_alert:
                        self._fallback_notification(title, message)

            # 控制台显示
            if self.enable_console_output:
                print(f"\n{'🔔' * 25}")
                print(f"📢 {title}")
                print(f"📝 {message}")
                print(f"⏰ 时间: {datetime.now().strftime('%H:%M:%S')}")
                print(f"{'🔔' * 25}\n")

        except Exception as e:
            self.logger.error(f"处理通知时发生错误: {e}")

    def _fallback_notification(self, title: str, message: str):
        """备用通知方式"""
        try:
            if self.enable_sound_alert:
                # 系统蜂鸣声
                for i in range(3):
                    print(f"\a")
                    time.sleep(0.3)

            if self.enable_console_output:
                # 控制台高亮提醒
                print("\n" + "🚨" * 50)
                print(f"🚨 {title} 🚨")
                print(f"📢 {message}")
                print("🚨" * 50 + "\n")
        except:
            pass

    def add_notification(self, notification_type: str, **kwargs):
        """添加通知到队列"""
        notification_data = {'type': notification_type, **kwargs}
        self.notification_queue.put(notification_data)

    def monitor_once(self):
        """执行一次监控"""
        # 检查日期是否变化，如果变天则重置基准
        current_date = datetime.now().date()
        if current_date != self.current_date:
            self.logger.info(f"检测到日期变化: {self.current_date} -> {current_date}")
            self.reset_baseline()
            self.current_date = current_date

        self.logger.info("开始执行监控...")

        # 获取数据
        raw_data = self.fetch_stock_data()
        if not raw_data:
            self.logger.warning("获取数据失败，跳过本次监控")

            # 如果连续失败次数过多，发送通知
            if self.network_stats['consecutive_failures'] >= 3:
                self.add_notification(
                    'network_error',
                    message=f"连续{self.network_stats['consecutive_failures']}次获取数据失败，请检查网络连接"
                )
            return

        # 解析数据
        current_plates, stock_details = self.parse_stock_data(raw_data)

        if not current_plates:
            self.logger.info("当前没有涨停股票数据")
            return

        # 第一次运行，设置基准数据
        if not self.baseline_set:
            self.baseline_plates = current_plates.copy()
            self.baseline_set = True
            self.save_baseline_data()

            total_stocks = sum(len(stocks) for stocks in self.baseline_plates.values())
            self.logger.info(f"设置今日基准数据完成，共{len(self.baseline_plates)}个板块，{total_stocks}只股票")
            if self.enable_console_output:
                print(f"📊 今日基准数据已设置，共{len(self.baseline_plates)}个板块，{total_stocks}只涨停股票")
            return

        # 分析新增股票并直接发送通知
        alert_plates = []

        for plate_id, current_stocks in current_plates.items():
            baseline_stocks = self.baseline_plates.get(plate_id, set())
            new_stocks = current_stocks - baseline_stocks

            if (
                    new_stocks
                    and len(baseline_stocks) >= self.min_plate_stocks
                    and plate_id not in self.alerted_plates
            ):
                plate_name = self.plate_names.get(plate_id, f"板块{plate_id}")
                # 记录该板块已预警
                self.alerted_plates.add(plate_id)
                # 获取股票名称
                stock_names = []
                for symbol in new_stocks:
                    stock_info = stock_details.get(symbol, {})
                    stock_names.append(stock_info.get('name', symbol))

                alert_plates.append({
                    'plate_id': plate_id,
                    'plate_name': plate_name,
                    'new_count': len(new_stocks),
                    'new_stocks': new_stocks,
                    'stock_names': stock_names
                })

                # 打印详细信息
                self._print_plate_details(plate_id, plate_name, new_stocks, stock_details)

        # 发送通知
        if alert_plates:
            self._handle_notifications(alert_plates)
            # 保存已预警板块记录
            self.save_baseline_data()
        else:
            self.logger.info("没有新的涨停股票")

    def _handle_notifications(self, alert_plates: list):
        """处理通知发送"""
        total_new_stocks = sum(plate['new_count'] for plate in alert_plates)
        self.logger.info(f"发现 {len(alert_plates)} 个板块有新涨停，共 {total_new_stocks} 只股票")

        # 多板块通知
        if len(alert_plates) > 1:
            plate_names = [plate['plate_name'] for plate in alert_plates]
            self.add_notification(
                'multi_plate',
                plate_count=len(alert_plates),
                total_count=total_new_stocks,
                plate_details=plate_names
            )

        # 单板块通知
        for plate_info in alert_plates:
            self.add_notification(
                'new_stocks',
                plate_name=plate_info['plate_name'],
                count=plate_info['new_count'],
                stock_details=plate_info['stock_names']
            )

    def _print_plate_details(self, plate_id: int, plate_name: str, new_stocks: set, stock_details: dict):
        """打印板块详细信息"""
        if self.enable_console_output:
            print(f"\n📈 {plate_name} 板块新增涨停:")
            for symbol in new_stocks:
                stock_info = stock_details.get(symbol, {})
                print(f"  • {stock_info.get('name', symbol)} ({symbol})")
                reason = stock_info.get('reason', '')
                if reason:
                    print(f"    理由: {reason}")

    def test_notification(self):
        """测试通知功能"""
        try:
            current_time = datetime.now().strftime('%H:%M:%S')
            if self.enable_desktop_notification:
                notification.notify(
                    title=f"🧪 通知测试 - {current_time}",
                    message="这是一个测试通知，如果您看到这个弹窗，说明通知功能正常工作！",
                    timeout=10,
                    app_name="股票监控系统"
                )
            if self.enable_console_output:
                print("✅ 测试通知已发送，请查看桌面弹窗")
            self.logger.info("测试通知发送成功")
        except Exception as e:
            if self.enable_console_output:
                print(f"❌ 测试通知发送失败: {e}")
            self.logger.error(f"测试通知发送失败: {e}")

    def reset_baseline(self):
        """重置基准数据"""
        self.baseline_plates.clear()
        self.baseline_set = False
        self.plate_names.clear()
        self.alerted_plates.clear()

        data_file = self.get_today_data_file()
        if os.path.exists(data_file):
            os.remove(data_file)

        self.logger.info("基准数据已重置")
        if self.enable_console_output:
            print("🔄 基准数据已重置，下次监控将重新设置基准")

    def get_system_status(self) -> Dict[str, Any]:
        """获取系统状态"""
        total_baseline = sum(len(stocks) for stocks in self.baseline_plates.values())

        # 计算成功率
        success_rate = 0
        if self.network_stats['total_requests'] > 0:
            success_rate = (self.network_stats['successful_requests'] / self.network_stats['total_requests']) * 100

        return {
            'current_date': self.current_date.strftime('%Y-%m-%d'),
            'data_file': self.get_today_data_file(),
            'baseline_plates': len(self.baseline_plates),
            'baseline_stocks': total_baseline,
            'baseline_set': self.baseline_set,
            'notification_queue_size': self.notification_queue.qsize(),
            'settings': {
                'monitor_interval': self.monitor_interval,
                'notification_interval': self.notification_interval,
                'notification_timeout': self.notification_timeout,
                'enable_proxy': self.enable_proxy,
                'filter_st_stocks': self.filter_st_stocks,
                'min_plate_stocks': self.min_plate_stocks,
            },
            'network_stats': {
                'total_requests': self.network_stats['total_requests'],
                'successful_requests': self.network_stats['successful_requests'],
                'failed_requests': self.network_stats['failed_requests'],
                'success_rate': f"{success_rate:.1f}%",
                'consecutive_failures': self.network_stats['consecutive_failures'],
                'last_success_time': self.network_stats['last_success_time'].strftime('%H:%M:%S') if self.network_stats[
                    'last_success_time'] else '无'
            }
        }

    def start_monitoring(self):
        """开始监控"""
        self.logger.info(f"开始板块涨停监控，检查间隔: {self.monitor_interval}秒")
        status = self.get_system_status()

        if self.enable_console_output:
            print(f"\n📊 板块涨停监控系统已启动")
            print(f"📅 监控日期: {status['current_date']}")
            print(f"⏰ 检查间隔: {self.monitor_interval}秒")
            print(f"🔔 通知间隔: {self.notification_interval}秒")
            print(f"⏱️ 通知显示时间: {self.notification_timeout}秒")
            print(f"🌐 代理设置: {'启用' if self.enable_proxy else '禁用'}")
            print(f"🚫 过滤ST股票: {'是' if self.filter_st_stocks else '否'}")
            print(f"📊 最小板块股票数: {self.min_plate_stocks}")
            print(f"📁 今日数据文件: {status['data_file']}")
            print(f"📋 日志文件: {self.log_file}")
            print(f"💡 可用命令: reset, test, status, plates, network, config, help")
            print(f"{'=' * 60}")

        # 启动输入监听线程
        if self.enable_console_output:
            input_thread = threading.Thread(target=self._input_listener, daemon=True)
            input_thread.start()

        try:
            while True:
                self.monitor_once()
                time.sleep(self.monitor_interval)
        except KeyboardInterrupt:
            self.logger.info("监控系统已停止")
            if self.enable_console_output:
                print("\n📴 监控系统已停止")
        except Exception as e:
            self.logger.error(f"监控系统发生错误: {e}")
            if self.enable_console_output:
                print(f"\n❌ 监控系统发生错误: {e}")

    def _input_listener(self):
        """输入监听线程"""
        while True:
            try:
                user_input = input().strip().lower()
                if user_input == 'reset':
                    self.reset_baseline()
                elif user_input == 'test':
                    self.test_notification()
                elif user_input == 'status':
                    status = self.get_system_status()
                    print(f"\n📊 系统状态:")
                    print(f"  监控日期: {status['current_date']}")
                    print(f"  数据文件: {status['data_file']}")
                    print(f"  基准板块数: {status['baseline_plates']}")
                    print(f"  基准股票数: {status['baseline_stocks']}")
                    print(f"  基准已设置: {'是' if status['baseline_set'] else '否'}")
                    print(f"  待发送通知数: {status['notification_queue_size']}")
                elif user_input == 'config':
                    status = self.get_system_status()
                    settings = status['settings']
                    print(f"\n⚙️ 配置信息:")
                    print(f"  监控间隔: {settings['monitor_interval']}秒")
                    print(f"  通知间隔: {settings['notification_interval']}秒")
                    print(f"  通知显示时间: {settings['notification_timeout']}秒")
                    print(f"  启用代理: {'是' if settings['enable_proxy'] else '否'}")
                    print(f"  过滤ST股票: {'是' if settings['filter_st_stocks'] else '否'}")
                    print(f"  最小板块股票数: {settings['min_plate_stocks']}")
                elif user_input == 'network':
                    status = self.get_system_status()
                    stats = status['network_stats']
                    print(f"\n🌐 网络状态:")
                    print(f"  总请求数: {stats['total_requests']}")
                    print(f"  成功请求数: {stats['successful_requests']}")
                    print(f"  失败请求数: {stats['failed_requests']}")
                    print(f"  成功率: {stats['success_rate']}")
                    print(f"  连续失败次数: {stats['consecutive_failures']}")
                    print(f"  最后成功时间: {stats['last_success_time']}")
                elif user_input == 'plates':
                    print(f"\n📋 监控板块列表:")
                    for plate_id, plate_name in self.plate_names.items():
                        stock_count = len(self.baseline_plates.get(plate_id, set()))
                        alerted = "✅" if plate_id in self.alerted_plates else "⏳"
                        print(f"  {alerted} {plate_name} (ID: {plate_id}) - {stock_count}只股票")
                elif user_input == 'help':
                    print(f"\n📖 可用命令:")
                    print(f"  reset   - 重置基准数据")
                    print(f"  test    - 测试桌面通知")
                    print(f"  status  - 查看系统状态")
                    print(f"  config  - 查看配置信息")
                    print(f"  network - 查看网络状态")
                    print(f"  plates  - 查看监控板块")
                    print(f"  help    - 显示帮助")
            except:
                pass


def main():
    """主函数"""
    try:
        print("🚀 正在启动股票监控系统...")
        print("📦 请确保已安装依赖: pip install plyer requests configparser")
        print("📄 配置文件: config.ini")
        print("=" * 50)

        monitor = StockMonitor()
        monitor.start_monitoring()

    except Exception as e:
        print(f"❌ 程序启动失败: {e}")
        print("💡 请检查网络连接和依赖安装")
        logging.error(f"程序启动失败: {e}")


if __name__ == "__main__":
    main()
